package com.cs.deviceManager.mqtt;

import com.cs.deviceManager.device.DeviceTypeManager;
import com.cs.deviceManager.device.DeviceEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 设备心跳监控器：定期检查设备是否超时未上报数据，自动标记为离线。
 */
public class DeviceHeartbeatMonitor {

    private static final Logger log = LoggerFactory.getLogger(DeviceHeartbeatMonitor.class);

    // 默认超时时间：60 秒
    private static final long DEFAULT_TIMEOUT_SECONDS = 60;

    // 存储设备最后活跃时间（毫秒）
    private final Map<String, Long> lastActiveTimeMap = new ConcurrentHashMap<>();

    // 存储设备类型
    private final Map<String, String> deviceTypeMap = new ConcurrentHashMap<>();

    // 在线状态（与 MqttUtil 共享）
    private final Map<String, DeviceStatus> deviceStatusMap;

    // 监听器列表
    private final CopyOnWriteArrayList<DeviceEventListener> listeners = new CopyOnWriteArrayList<>();

    // 调度执行器
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    private final ScheduledFuture<?> monitorTask;
    private final long defaultTimeoutSeconds;

    private final AtomicBoolean started = new AtomicBoolean(false);

    public DeviceHeartbeatMonitor(Map<String, DeviceStatus> deviceStatusMap) {
        this(deviceStatusMap, DEFAULT_TIMEOUT_SECONDS, null);
    }

    public DeviceHeartbeatMonitor(Map<String, DeviceStatus> deviceStatusMap, long defaultTimeoutSeconds,
                                  DeviceEventListener listener) {
        this.deviceStatusMap = deviceStatusMap;
        this.defaultTimeoutSeconds = defaultTimeoutSeconds;

        this.monitorTask = scheduler.scheduleAtFixedRate(
                this::checkTimeoutDevices,
                10, 10, TimeUnit.SECONDS  // 每 10 秒检查一次
        );
        this.addListener(listener);
        started.set(true);
        log.info("设备心跳监控器已启动，默认超时时间：{} 秒", defaultTimeoutSeconds);
    }

    /**
     * 设备上报数据或上线时调用，刷新心跳时间
     *
     * @param deviceId   设备ID
     * @param deviceType 设备类型（可选）
     */
    public void heartbeat(String deviceId, String deviceType) {
        if (!started.get()) {
            log.warn("心跳监控器未启动，忽略心跳: {}", deviceId);
            return;
        }

        long now = System.currentTimeMillis();
        lastActiveTimeMap.put(deviceId, now);
        if (deviceType != null) {
            deviceTypeMap.put(deviceId, deviceType);
        }

        // 仅在状态变化时更新状态并通知监听器
        if (deviceStatusMap.putIfAbsent(deviceId, DeviceStatus.ONLINE) != DeviceStatus.ONLINE) {
            // 通知监听器
            listeners.forEach(listener -> {
                try {
                    listener.onDeviceConnected(deviceId);
                } catch (Exception e) {
                    log.error("监听器 onDeviceConnected 执行异常", e);
                }
            });
        }
    }

    /**
     * 设备上报数据时调用（不指定设备类型）
     * @param deviceId 设备ID
     */
    public void heartbeat(String deviceId) {
        heartbeat(deviceId, null);
    }

    /**
     * 手动标记设备为离线
     *
     * @param deviceId 设备ID
     */
    public void markOffline(String deviceId) {
        if (!started.get()) {
            log.warn("心跳监控器未启动，忽略离线标记: {}", deviceId);
            return;
        }

        lastActiveTimeMap.remove(deviceId);
        deviceTypeMap.remove(deviceId);
        deviceStatusMap.put(deviceId, DeviceStatus.OFFLINE);

        // 通知监听器
        listeners.forEach(listener -> {
            try {
                listener.onDeviceDisconnected(deviceId);
            } catch (Exception e) {
                log.error("监听器 onDeviceDisconnected 执行异常", e);
            }
        });
    }

    /**
     * 检查超时设备并标记为离线
     */
    private void checkTimeoutDevices() {
        long now = System.currentTimeMillis();
        Set<Map.Entry<String, Long>> entries = lastActiveTimeMap.entrySet();

        for (Map.Entry<String, Long> entry : entries) {
            String deviceId = entry.getKey();
            long lastActiveTime = entry.getValue();
            String deviceType = deviceTypeMap.get(deviceId);

            // 获取设备类型的超时时间
            long timeoutSeconds = getDeviceTypeTimeout(deviceType);
            long timeoutMillis = timeoutSeconds * 1000;

            // 判断是否超时
            if (now - lastActiveTime > timeoutMillis) {
                log.info("设备 {} 超时未上报数据，标记为离线", deviceId);
                markOffline(deviceId);
            }
        }
    }

    /**
     * 获取设备类型的超时时间
     *
     * @param deviceType 设备类型
     * @return 超时时间（秒）
     */
    public long getDeviceTypeTimeout(String deviceType) {
        if (deviceType != null) {
            return DeviceTypeManager.getDeviceTypeTimeout(deviceType);
        }
        return defaultTimeoutSeconds;
    }

    /**
     * 添加监听器
     *
     * @param listener 监听器
     */
    public void addListener(DeviceEventListener listener) {
        listeners.addIfAbsent(listener);
    }

    /**
     * 移除监听器
     *
     * @param listener 监听器
     */
    public void removeListener(DeviceEventListener listener) {
        listeners.remove(listener);
    }

    /**
     * 关闭监控器
     */
    public void shutdown() {
        if (started.compareAndSet(true, false)) {
            monitorTask.cancel(true);
            scheduler.shutdown();
            log.info("设备心跳监控器已关闭");
        }
    }
}